package com.SingleLinkedList;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class SingleLinkedListImplementation<E> implements SingleLinkedList<E> {

	private class Node {
		private E data;
		private Node next;

		Node(E data) {
			this.data = data;
		}
	}

	private Node first;
	private Node last;
	private int size;

	private class IteratorImpl implements Iterator<E> {
		private Node currentNode = null;
		private Node previousNode = null;
		private boolean removeFlag = false;

		public boolean hasNext() {
			if (currentNode == null)
				return first != null;
			return currentNode.next != null;
		}

		public E next() {
			if (this.hasNext()) {
				previousNode = currentNode;
				if (currentNode == null)
					currentNode = first;
				else
					currentNode = currentNode.next;
				removeFlag = true;
				return currentNode.data;
			}
			throw new NoSuchElementException(
					"No next element in the collection");
		}

		public void remove() {
			if (removeFlag) {
				currentNode.data = null;
				if (currentNode == first) {
					first = currentNode.next;
					currentNode.next = null;
					currentNode = null;
				} else if (currentNode == last) {
					last = previousNode;
					previousNode.next = null;
					currentNode = previousNode;
				} else {
					previousNode.next = currentNode.next;
					currentNode.next = null;
					currentNode = previousNode;
				}
				removeFlag = false;
				size--;
			} else {
				throw new IllegalStateException(
						"Invoke next() before invoking remove()");
			}
		}
	}

	@Override
	public void add(E e) {
		if (e == null)
			throw new IllegalArgumentException("Null argument is not allowed");
		Node newNode = new Node(e);
		if (first == null) {
			first = newNode;
		} else {
			last.next = newNode;
		}
		last = newNode;
		size++;
	}

	@Override
	public void clear() {
		Iterator<E> iterator = new IteratorImpl();
		while (iterator.hasNext()) {
			iterator.next();
			iterator.remove();
		}
	}

	@Override
	public boolean remove(Object o) {
		if (o == null)
			return false;
		IteratorImpl iterator = new IteratorImpl();
		while (iterator.hasNext()) {
			if (iterator.next().equals(o)) {
				iterator.remove();
				return true;
			}
		}
		return false;
	}

	@Override
	public int size() {
		return size;
	}

	@Override
	public boolean contains(Object o) {
		if (o == null)
			return false;
		IteratorImpl iterator = new IteratorImpl();
		while (iterator.hasNext()) {
			if (iterator.next().equals(o)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Iterator<E> iterator() {
		return new IteratorImpl();
	}
}
